package sbt.transformation;

import java.util.Collection;
import java.util.Map.Entry;
import java.util.SortedMap;

import sbt.metamodel.AbstractTrace;
import sbt.metamodel.MetamodelException;
import sbt.metamodel.Obsel;
import sbt.metamodel.TransformedTrace;
import sbt.metamodel.TraceListener.ChangeType;

/**
 * I implement a temporal filter transformation.
 * 
 * I only accept one source. I accept parameters 'from' and or 'to', 
 * representing the inclusive bounds of the temporal interval to copy. 
 * @author pa
 *
 */
public class Filter extends AbstractCopier {

	private long from, to;
	private AbstractTrace source;
	private long lastSeen; // index of the last seen obsel
	private boolean pending, done;
	
	protected void init(TransformedTrace target) {
		super.init(target); // calls notifyMetadataChange...
		// ... so source, from and to are set
		pending = true;
		lastSeen = -1;
		done = false;
	}

	@Override
	protected String computeOrigin() {
		return getTarget().getOrigin();
	}

	@Override
	protected String computeModel() {
		return getTarget().getModel();
	}

	@Override
	protected void updateObsels() {
		SortedMap<Long, Obsel> srcObsels = source.getObselsById();
		Collection<Obsel> unseen = srcObsels.tailMap(lastSeen+1).values();
		for (Obsel o: unseen) {
			if (o.getEnd() > to) {
				done = true;
				break;
			}
			if (o.getBegin() >= from) {
				copyObsel(o);
			}
		}
		lastSeen = srcObsels.lastKey();
		pending = false;
	}

	@Override
	protected void notifyObselsChange(AbstractTrace trace, ChangeType type) {
		assert (trace == source);
		if (type != ChangeType.MONOTONIC_TEMPORAL) {
			done = false;
			if (type == ChangeType.NON_MONOTONIC) {
				clearTargetObsels();
				lastSeen = -1;
			}
		}
		if (!done) {
			pending = true;
		}
	}

	@Override
	protected void notifyMetadataChange() {
		from = Long.MIN_VALUE;
		to = Long.MAX_VALUE;
		if (getTarget().getSources().size() > 1) {
			throw new MetamodelException("Too many sources");
		}
		for (AbstractTrace t: getTarget().getSources()) {
			source = t;
		}
		for (Entry<String, Object> e: getTarget().getParameters().entrySet()) {
			if (e.getKey() == "from") {
				from = (Long) e.getValue();
			}
			else if (e.getKey() == "to") {
				to = (Long) e.getValue();
			}
			else {
				throw new MetamodelException("Unknown parameter " + e.getKey());
			}
		}
	}
	
	@Override
	protected boolean hasPendingChanges() {
		return pending;
	}

}
